import mod_dungeon,random,mod_direction
from features.wall import wallPrototype
from features.door import placeDoor,doorPrototype
from features.barrel import barrelPrototype

class Room:
    def __init__(self):
        self.interiorTiles = set()
        self.wallTiles = set()
        self.cornerTiles = set()
        self.adjacentRooms = set()
        self.rect = None
        
    def absorb(self,otherRoom):
        
        allWalls = self.wallTiles | otherRoom.wallTiles
        commonWalls = self.wallTiles & otherRoom.wallTiles
        self.interiorTiles.update(otherRoom.interiorTiles | commonWalls)
        
        self.wallTiles,self.cornerTiles = findWallsAndCorners(self.interiorTiles)
        
#        self.wallTiles = allWalls - commonWalls
#        
#        formerCorners = self.cornerTiles | otherRoom.cornerTiles
#        newCorners = set()
#        
#        for tile in formerCorners:
#            hasNeighborWall = False
#            for dir in mod_direction.CARDINALDIRS:
#                neighbor = mod_direction.getNeighbor(tile,dir)
#                if neighbor in self.wallTiles:
#                    hasNeighborWall = True
#                    break
#            if not hasNeighborWall:
#                #Floor
#                self.interiorTiles.add(tile)
#            else:
#                hasNeighborFloor = False
#                for dir in mod_direction.CARDINALDIRS:
#                    neighbor = mod_direction.getNeighbor(tile,dir)
#                    if neighbor in self.interiorTiles:
#                        hasNeighborFloor = True
#                        break
#                if not hasNeighborFloor: # No neighbor floors or walls.
#                    # Corner
#                    newCorners.add(tile)
#                else:
#                    # Wall
#                    self.wallTiles.add(tile)
#                    
#        self.cornerTiles = newCorners
        
        for room in otherRoom.adjacentRooms:
            room.adjacentRooms.remove(otherRoom)
            room.adjacentRooms.add(self)
            self.adjacentRooms.add(room)
        
        #self.adjacentRooms.remove(otherRoom)
        self.adjacentRooms.remove(self)
        if len(self.adjacentRooms) == 0:
            print 'UH OH!'
    
    def __str__(self):
        return str(self.rect)
    
    def split(self):
        if self.rect:
            interiorTopLeft,interiorBottomRight = self.rect
            width = interiorBottomRight[0] - interiorTopLeft[0] + 1
            height = interiorBottomRight[1] - interiorTopLeft[1] + 1
            splittable = []
            if width >= 7:
                # Splittable via vertical wall
                splittable.append(0)
            if height >= 7:
                # Splittable via horizontal wall
                splittable.append(1)
            if splittable:
                #print width,height
                newRoom = Room()
                splitType = random.choice(splittable)
                splitPoint = random.randint(interiorTopLeft[splitType]+3,interiorBottomRight[splitType]-3)
                
#                cornerTilesToRemove = set()
#                for tile in self.cornerTiles: # Divide up the corner tiles
#                    if tile[splitType] == splitPoint:
#                        cornerTilesToRemove.add(tile)
#                    if tile[splitType] > splitPoint:
#                        cornerTilesToRemove.add(tile)
#                        newRoom.cornerTiles.add(tile)
#                for tile in cornerTilesToRemove:
#                    self.cornerTiles.remove(tile)
#                
#                
#                wallTilesToRemove = set() # Remove these after the loop, since it won't let us do it during it.
#                for tile in self.wallTiles: # Divide up the wall tiles
#                    if tile[splitType] == splitPoint:
#                        wallTilesToRemove.add(tile)
#                        self.cornerTiles.add(tile)
#                        newRoom.cornerTiles.add(tile)
#                    elif tile[splitType] > splitPoint:
#                        wallTilesToRemove.add(tile)
#                        newRoom.wallTiles.add(tile)
#                for tile in wallTilesToRemove:
#                    self.wallTiles.remove(tile)
                    
#                for room in self.adjacentRooms:
#                    room.wallTiles = room.wallTiles - wallTilesToRemove
                
                interiorTilesToRemove = set()        
                for tile in self.interiorTiles: # Divide up the interior tiles, make new wall tiles
                    if tile[splitType] == splitPoint:
                        interiorTilesToRemove.add(tile)
                        self.wallTiles.add(tile)
                        newRoom.wallTiles.add(tile)
                    elif tile[splitType] > splitPoint:
                        interiorTilesToRemove.add(tile)
                        newRoom.interiorTiles.add(tile)
                for tile in interiorTilesToRemove:
                    self.interiorTiles.remove(tile)
                
                self.wallTiles,self.cornerTiles = findWallsAndCorners(self.interiorTiles)
                newRoom.wallTiles,newRoom.cornerTiles = findWallsAndCorners(newRoom.interiorTiles)
                
                
                
                adjacentRoomsToRemove = set()
                for room in self.adjacentRooms:
                    if room.wallTiles & newRoom.wallTiles: # If the new room shares a wall...
                        newRoom.adjacentRooms.add(room)
                        room.adjacentRooms.add(newRoom)
                    if not (room.wallTiles & self.wallTiles): # If we no longer share a wall...
                        adjacentRoomsToRemove.add(room)
                for room in adjacentRoomsToRemove:
                    self.adjacentRooms.remove(room)
                    room.adjacentRooms.remove(self)
                
                self.adjacentRooms.add(newRoom)
                newRoom.adjacentRooms.add(self)
                
                # Update rects for future splits
                if splitType == 0: # Vertical line
                    self.rect = (interiorTopLeft,(splitPoint-1,interiorBottomRight[1]))
                    newRoom.rect = ((splitPoint+1,interiorTopLeft[1]),interiorBottomRight)
                else: # Horizontal line
                    self.rect = (interiorTopLeft,(interiorBottomRight[0],splitPoint-1))
                    newRoom.rect = ((interiorTopLeft[0],splitPoint+1),interiorBottomRight)
                return newRoom
        return None

def createRectangularRoom(interiorTopLeft,interiorBottomRight):
    room = Room()
    
    for x in range(interiorTopLeft[0],interiorBottomRight[0]+1):
        for y in range(interiorTopLeft[1],interiorBottomRight[1]+1):
            room.interiorTiles.add((x,y))
    room.wallTiles,room.cornerTiles = findWallsAndCorners(room.interiorTiles)
    
#    for x in range(interiorTopLeft[0],interiorBottomRight[0]+1):
#        for y in range(interiorTopLeft[1],interiorBottomRight[1]+1):
#            room.interiorTiles.add((x,y))
#        room.wallTiles.add((x,interiorTopLeft[1]-1))
#        room.wallTiles.add((x,interiorBottomRight[1]+1))
#    for y in range(interiorTopLeft[1],interiorBottomRight[1]+1):
#        room.wallTiles.add((interiorTopLeft[0]-1,y))
#        room.wallTiles.add((interiorBottomRight[0]+1,y))
    room.rect = (interiorTopLeft,interiorBottomRight)
    return room
    
    
def findWallsAndCorners(interiorTiles):
    wallTiles = set()
    cornerTiles = set()
    for tile in interiorTiles:
        for dir in mod_direction.CARDINALDIRS:
            wallTiles.add(mod_direction.getNeighbor(tile,dir))
        for dir in mod_direction.DIAGONALDIRS:
            cornerTiles.add(mod_direction.getNeighbor(tile,dir))
    wallTiles = wallTiles - interiorTiles
    cornerTiles = (cornerTiles - interiorTiles) - wallTiles
    return wallTiles,cornerTiles

    
def createLevel(size,game,number):
    width,height = size
    level = mod_dungeon.Level(size,game,number)
    mod_dungeon.setUpEmptyLevel(level)
    
    rooms = []
    splittableRooms = []
    bigRoom = createRectangularRoom((1,1),(size[0]-2,size[1]-2))
    rooms.append(bigRoom)
    splittableRooms.append(bigRoom)
    del bigRoom
    # Split some rooms:
    for i in range(15):
        if not splittableRooms:
            break
        room = random.choice(splittableRooms)
        newRoom = room.split()
        if newRoom:
            rooms.append(newRoom)
            splittableRooms.append(newRoom)
        else:
            splittableRooms.remove(room)
    del splittableRooms
    
    # Join some rooms:
    for i in range(random.randint(3,5)):
        room = random.choice(rooms)
        otherRoom = random.choice(list(room.adjacentRooms))
        room.absorb(otherRoom)
        rooms.remove(otherRoom)
    del room
    del otherRoom
    
    print len(rooms)
    
    allTiles = set()
    interiorTiles = set()
    allWalls = set()
    allCorners = set()
    
    for x in range(width):
        for y in range(height):
            allTiles.add((x,y))
    
    for room in rooms:
        interiorTiles.update(room.interiorTiles)
        allWalls.update(room.wallTiles)
        allWalls.update(room.cornerTiles)

    #allWalls = allTiles - interiorTiles
    
    for tile in allWalls:
        wall = wallPrototype.instantiate()
        wall.setLocation((level,tile))
    del allWalls
    
    # Set the starting point to a random tile at the bottom of the level:
    startingRoom = None
    while not startingRoom:
        startX = random.randint(1,width-2)
        startY = height-1
        startPoint = (startX,startY)
        startingRoom = getRoomAtPoint(level,startPoint,rooms)
        
    connectRooms(level,startingRoom,rooms)
    
    entityThere = level.entityMap.get(startPoint)
    if entityThere:
        entityThere.setLocation(None)
    
    fixWalls(level)
    
    # Place traps, items, and baddies:
    allSpaces = set(level.spaceMap.keys())
    validSpaces = list(allSpaces - set(level.entityMap.keys()))
    
    # Place level portal
#    coords = random.choice(validSpaces)
#    placeItem(level,mod_trap.NextLevelTrap(),coords)
#    validSpaces.remove(coords)
    
    # Place some items
    #for n in range(random.randint(3,5)):
    for n in range(random.randint(10,15)):
        coords = random.choice(validSpaces)
        item = mod_dungeon.chooseItem()
        item.setLocation((level,coords))
    
    # Place some enemies/barrels
    #for n in range(random.randint(13,16)):
    for n in range(random.randint(5,8)):
        location = (level,random.choice(validSpaces))
        entity = mod_dungeon.chooseEnemy()
        entity.setLocation(location)
        validSpaces.remove(location[1])
    
    mod_dungeon.printLevel(level)
    return level

def getRoomAtPoint(level,point,rooms):
    for room in rooms:
        if point in room.interiorTiles or point in room.wallTiles:
            return room
    return None

def connectRooms(level,startingRoom,rooms):
    currentRoom = startingRoom
    for room in rooms:
        room.visited = False
    finished = False
    roomStack = [currentRoom]
    while not finished:
        currentRoom.visited = True
        unvisitedNeighbors = []
        
        # Find all unvisited neighbors:
        for neighbor in currentRoom.adjacentRooms:
            if not neighbor.visited:
                unvisitedNeighbors.append(neighbor)
        if unvisitedNeighbors:
            nextRoom = random.choice(unvisitedNeighbors)
            makeDoorway(level,currentRoom,nextRoom)
            roomStack.append(nextRoom)
            currentRoom = nextRoom
        else:
            if roomStack:
                currentRoom = roomStack.pop()
            else:
                finished = True
            

def makeDoorway(level,room1,room2):
    sharedWalls = room1.wallTiles & room2.wallTiles # Set intersection
    doorTile = random.choice(list(sharedWalls))
    entity = level.entityMap.get(doorTile)
    if entity:
        entity.setLocation(None)
    if random.choice([True,False]):
        placeDoor(level,doorTile)
    
    
def fixWalls(level):
    wallsToRemove = set()
    for coords,entity in level.entityMap.iteritems():
        if entity.prototype == wallPrototype:
            neighborWallDirs = ''
            for dir in mod_direction.CARDINALDIRS:
                neighbor = mod_direction.getNeighbor(coords,dir)
                otherEntity = level.entityMap.get(neighbor)
                # If the neighbor space has a wall or a door...
                if otherEntity and (otherEntity.prototype == doorPrototype or otherEntity.prototype == wallPrototype):
                    neighborWallDirs += dir.name
            
            if len(neighborWallDirs) == 0:
                wallsToRemove.add(entity)
            else:
#                if neighborWallDirs == 'N' or neighborWallDirs == 'S':
#                    neighborWallDirs = 'NS'
#                elif neighborWallDirs == 'E' or neighborWallDirs == 'W':
#                    neighborWallDirs = 'EW'
                entity.glyph = 'WALL_'+neighborWallDirs
                #print entity.__dict__
                #print entity.glyph
                
    for wall in wallsToRemove:
        #print 'Removing freestander'
        wall.setLocation(None)
        